package is.glif.fenius.translators.glambda;

import is.glif.fenius.util.ConfigManager;

import is.glif.fenius.api.FCancelReq;
import is.glif.fenius.api.FCancelResp;
import is.glif.fenius.api.FCreateReq;
import is.glif.fenius.api.FCreateResp;
import is.glif.fenius.api.FListReq;
import is.glif.fenius.api.FListResp;
import is.glif.fenius.api.FExternalFault;
import is.glif.fenius.api.FInternalFault;
import is.glif.fenius.api.FReservationParams;
import is.glif.fenius.api.Translator;

import java.util.Calendar;
import java.util.Hashtable;
import java.util.Enumeration;
import java.util.Date;
import java.util.TimeZone;
import java.util.LinkedList;
import java.util.ArrayList;
import java.util.Map;
import java.util.UUID;

import net.glambda.schemas.gnswsi3._2008._12.rdl.Timeframe_Type;
import net.glambda.schemas.gnswsi3._2008._12.rdl.ExactTimeframe_Type;
import net.glambda.schemas.gnswsi3._2008._12.rdl.CoallocationTimeframe_Type;
import net.glambda.schemas.gnswsi3._2008._12.rdl.CoallocationTimeSpecification_Type;
import net.glambda.schemas.gnswsi3._2008._12.rdl.CoallocationTimeSpecification_Type;
import net.glambda.schemas.gnswsi3._2008._12.rdl.ReservationStatus_Type;
import net.glambda.schemas.gnswsi3._2008._12.rdl.ResourceAttribute_Type;
import net.glambda.schemas.gnswsi3._2008._12.rdl.ReservationResources_Type;
import net.glambda.schemas.gnswsi3._2008._12.ndl.NetworkResources_Type;
import net.glambda.schemas.gnswsi3._2008._12.rdl.ResourceRequirements_Type;
import net.glambda.schemas.gnswsi3._2008._12.rdl.TimeSpecification_Type;

import net.glambda.schemas.gnswsi3._2008._12.ndl.MediaType_Type;
import net.glambda.schemas.gnswsi3._2008._12.ndl.TerminationPoint_Type;
import net.glambda.schemas.gnswsi3._2008._12.ndl.Endpoint_Type;
import net.glambda.schemas.gnswsi3._2008._12.ndl.Route_Type;
import net.glambda.schemas.gnswsi3._2008._12.ndl.GeneralBW_Type;
import net.glambda.schemas.gnswsi3._2008._12.ndl.BWUnit_Type;
import net.glambda.schemas.gnswsi3._2008._12.ndl.Bandwidth_Type;
import net.glambda.schemas.gnswsi3._2008._12.ndl.SwitchingScheme_Type;
import net.glambda.schemas.gnswsi3._2008._12.ndl.EthernetParameters_Type;
import net.glambda.schemas.gnswsi3._2008._12.ndl.MPLSParameters_Type;
import net.glambda.schemas.gnswsi3._2008._12.ndl.IPParameters_Type;
import net.glambda.schemas.gnswsi3._2008._12.ndl.PathProperties_Type;
import net.glambda.schemas.gnswsi3._2008._12.ndl.Path_Type;
import net.glambda.schemas.gnswsi3._2008._12.ndl.NetworkResourceSLADocument_Type;

import org.ietf.jgss.GSSCredential;

import javax.jws.WebService;
import javax.jws.WebMethod;

@WebService
public class GlambdaTranslator implements Translator {
    static public boolean debug = true;
    private static int no = 0;
    static String domain = null;
    static String service = null;
    static String DN = null;
    static GSSCredential credential = null;
    static int adjustTime = 0;

    private static Hashtable<String, ResourceManagerClient> rms;
    private static Hashtable<String, FReservationParams> reservation;
    private static Hashtable<String, String> uuid;
    private static LinkedList<String> ids;

    static {
	System.err.println("== GlambdaTranslator:static {");
	rms = new Hashtable<String, ResourceManagerClient>();
	reservation = new Hashtable<String, FReservationParams>();
	uuid = new Hashtable<String, String>();
	ids = new LinkedList<String>();
	TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
	// TimeZone.setDefault(TimeZone.getTimeZone("GMT-8"));
	System.err.println("== GlambdaTranslator:static }");
    }

    public GlambdaTranslator() 
    {
	System.err.println("== GlambdaTranslator:new");
    }

    @WebMethod
    public FCreateResp create(FCreateReq r) 
	throws FInternalFault, FExternalFault
    {
        loadConfig();

	System.err.println("== GlambdaTranslator:create() in");
	no ++;
	String coid = "coid-" + no;
	String reservationId = "[" + no + "]:";

	FReservationParams resvParams = r.getResvParams();
	// reservationId = resvParams.getReservationId() + reservationId;
	reservationId = resvParams.getReservationId();
	resvParams.setReservationId(reservationId);
	Date activationTime = resvParams.getActivationTime();
	Integer duration = resvParams.getDuration();
	Integer bandwidth = resvParams.getBandwidth() * 1000000;
	String srcEndpoint = resvParams.getSrcEndpoint();
	String dstEndpoint = resvParams.getDstEndpoint();
        String svlan = resvParams.getVlanTag();
	Integer vlan = Integer.valueOf(svlan);
	String status = resvParams.getStatus();

	if (debug) {
	    System.err.println("== GlambdaTranslator:id      =" + reservationId);
	    System.err.println("== GlambdaTranslator:date    =" + activationTime);
	    System.err.println("== GlambdaTranslator:duration=" + duration);
	    System.err.println("== GlambdaTranslator:bw      =" + bandwidth);
	    System.err.println("== GlambdaTranslator:vlan    =" + vlan);
	    System.err.println("== GlambdaTranslator:a-point =" + srcEndpoint);
	    System.err.println("== GlambdaTranslator:z-point =" + dstEndpoint);
	}

	Calendar startTime = Calendar.getInstance();
	startTime.setTime(activationTime);
	startTime.set(Calendar.MILLISECOND, 0);
	if (adjustTime != 0) {
	    startTime.add(Calendar.HOUR_OF_DAY, adjustTime);
	}
	Calendar endTime = (Calendar) startTime.clone();
	endTime.set(Calendar.MILLISECOND, 0);
	endTime.add(Calendar.SECOND, duration);
	ExactTimeframe_Type etf = new ExactTimeframe_Type(startTime, endTime);
	
	CoallocationTimeframe_Type[] ctf = new CoallocationTimeframe_Type[1];
        ctf[0] = new CoallocationTimeframe_Type(coid, (Timeframe_Type) etf);
	
	CoallocationTimeSpecification_Type cts =
            new CoallocationTimeSpecification_Type(coid);
        TimeSpecification_Type ts = (TimeSpecification_Type) cts;
	    
	MediaType_Type media = null;
        TerminationPoint_Type[] a_tp = new TerminationPoint_Type[1];
        a_tp[0] = new TerminationPoint_Type(srcEndpoint, media);
        TerminationPoint_Type[] z_tp = new TerminationPoint_Type[1];
        z_tp[0] = new TerminationPoint_Type(dstEndpoint, media);
        Endpoint_Type a_ep = new Endpoint_Type(domain, a_tp);
        Endpoint_Type z_ep = new Endpoint_Type(domain, z_tp);
            
	Route_Type route = null;

        GeneralBW_Type minbw = new GeneralBW_Type(bandwidth, BWUnit_Type.bps);
        GeneralBW_Type maxbw = new GeneralBW_Type(bandwidth, BWUnit_Type.bps);
        GeneralBW_Type peakbw = new GeneralBW_Type(bandwidth, BWUnit_Type.bps);
        GeneralBW_Type guardbw = new GeneralBW_Type(bandwidth, BWUnit_Type.bps);
	Bandwidth_Type bw = new Bandwidth_Type(minbw, maxbw, peakbw, guardbw);

	Float avail = new Float(0);
        Integer delay = new Integer(10);
        SwitchingScheme_Type sw = SwitchingScheme_Type.ETHERNET;
        EthernetParameters_Type ether = new EthernetParameters_Type
            (new Integer(1500),   vlan,        new Integer(0));
	/*        MTU             VLAN ID           COS      */

	MPLSParameters_Type mpls = null;
        IPParameters_Type ip = null;
        PathProperties_Type prop = new PathProperties_Type
            (avail, delay, sw, ether, mpls, ip);
        Path_Type path = new Path_Type(a_ep, z_ep, route, bw, prop);

	NetworkResourceSLADocument_Type slad = null;
        ReservationStatus_Type rstatus = null;
        ResourceAttribute_Type[] attr = null;

        ReservationResources_Type[] rr = new ReservationResources_Type[1];
        NetworkResources_Type nr1 = new NetworkResources_Type
	    (null, rstatus, ts, attr, path, slad);
	// (reservationId, rstatus, ts, attr, path, slad);
	

	rr[0] = (ReservationResources_Type) nr1;
	ResourceRequirements_Type request = new ResourceRequirements_Type(ctf, rr);
	reservation.put(reservationId, resvParams);
	
	String subkey = reservationId;
	try {
	    if (debug) System.err.println("== GlambdaTranslator:reserve start.");
	    ResourceManagerClient nrm = new ResourceManagerClient
		(service, DN, null);
	    nrm.reserve(request);
	    rms.put(reservationId, nrm);
	    ids.add(reservationId);
	    if (debug) System.err.println("== GlambdaTranslator:reserve end.");

	    ReservationResources_Type[] rrt = nrm.getReservationResource();
	    Integer vlanid =  new Integer(0);

	    if (rrt[0] != null) {
		subkey = rrt[0].getReservationID();
		if (debug) System.err.println("== rID:" + reservationId + ", uuid:" + subkey);
		NetworkResources_Type nrt = (NetworkResources_Type) rrt[0];
		vlanid = nrt.getPath().getPathProperties().getEthernetParameters().getVLANTagID();
		System.err.println("== GlambdaTranslator:vlan    =" + vlanid);
	    }

	    uuid.put(reservationId, subkey);
	    resvParams.setStatus("RESERVED");
	    resvParams.setVlanTag(vlanid.toString());
	} catch (Exception ex) {
	    if (debug) System.err.println("== GlambdaTranslator:reserve exception.");
	    resvParams.setStatus("FAILED");
	    ex.printStackTrace();
	    FInternalFault f = new FInternalFault();
	    f.setMessage(ex.toString());
	    throw f;
	}

	FCreateResp result = new FCreateResp();
	result.setResvParams(resvParams);
	System.err.println("== GlambdaTranslator:create() out");
        return result;
    }
	
    @WebMethod
    public FListResp list(FListReq r)
	throws FInternalFault, FExternalFault
    {
	int size = ids.size();
	ArrayList<FReservationParams> rps = new ArrayList<FReservationParams>();

	for (int i = 0; i < size; i++) {
            String key = ids.get(i);
	    FReservationParams rp = reservation.get(key);
	    if (rp == null) continue;
	    rps.add(rp);
	    ResourceManagerClient nrm = rms.get(key);
	    if (nrm == null) continue;

	    String[] subkeys = new String[1];
	    subkeys[0] = uuid.get(key);
	    System.err.println("== rID:" + key + ", uuid=" + subkeys[0]);
	    /* subkeys[0] = key; */
	    try {
		ReservationStatus_Type[] rs = 
		    nrm.getReservationStatus(subkeys);
		// rp.setStatus(rs[0].toString());
		/*  not work
		if (rs[0] == ReservationSatuts_Type.Released ||
		    rs[0] == ReservationSatuts_Type.Error) {
		*/

		if (rs == null || rs[0] == null || rs[0].toString().equals("Error")) {
		    rp.setStatus("FAILED");
		    rms.remove(key);
		    uuid.remove(key);
		}

		String s = rs[0].toString();
		if (s.equals("Released") && ! rp.getStatus().equals("CANCELLED")) {
		    rp.setStatus("FINISHED");
		    rms.remove(key);
		    uuid.remove(key);
		} else if(s.equals("Reserved")) {
		    rp.setStatus("RESERVED");
		} else if(s.equals("Created")) {
		    rp.setStatus("REQUESTING");
		} else if(s.equals("Activated")) {
		    rp.setStatus("ACTIVE");
		} else {
		    System.err.println("== Unknown Status:" + s);
		}
	    } catch (Exception ex) {
		rms.remove(key);
		uuid.remove(key);
		rp.setStatus("FAILED");

		ex.printStackTrace();
	    }
	}

	FListResp result = new FListResp();
	result.setReservations(rps);
        return result;
    }
    
    public FCancelResp cancel(FCancelReq r) throws FInternalFault, FExternalFault {
	FReservationParams resvParams = r.getResvParams();
	String reservationId  = resvParams.getReservationId();

	ResourceManagerClient nrm = rms.get(reservationId);
	FReservationParams rp = reservation.get(reservationId);
	String[] subkeys = new String[1];
	subkeys[0] = uuid.get(reservationId);
	System.err.println("== rID:" + reservationId + ", uuid=" + subkeys[0]);

	if (nrm == null || rp == null) {
	    rp.setStatus("FAILED");
	    FCancelResp cr = new FCancelResp();
	    cr.setResvParams(rp);
	    return cr;
	}

	try {
	    if (debug) System.err.println("== GlambdaTranslator:release start.");
	    nrm.release(subkeys);
	    if (debug) System.err.println("== GlambdaTranslator:release end.");
	} catch (Exception ex) {
	    if (debug) System.err.println("== GlambdaTranslator:release exception.");
	    ex.printStackTrace();

	    rp.setStatus("FAILED");
	    rms.remove(reservationId);
	    uuid.remove(reservationId);
	    FInternalFault f = new FInternalFault();
	    f.setMessage(ex.toString());
	    throw f;
	}
	rms.remove(reservationId);
	uuid.remove(reservationId);
	rp.setStatus("CANCELLED");
	FCancelResp cr = new FCancelResp();
	cr.setResvParams(rp);
	return cr;
    }

    private void loadConfig() throws FInternalFault {
	if (service == null) {
	    System.err.println("== loadConfig start");

	    // domain = "AIST-NRM";
	    // service = "http://163.220.30.150:8080/wsrf/services/gridars/rms/ReservationFactoryService";
	    // service = "http://163.220.30.148:8080/wsrf/services/gridars/rms/ReservationFactoryService";
	    // DN = "fenius";
	    // credential = null;

	    try {
		ConfigManager cm = ConfigManager.getInstance();
		Map config = cm.getConfiguration();
		Map nrmConfig = (Map) config.get("glambda-service");
		service = (String) nrmConfig.get("service");
		domain = (String) nrmConfig.get("domain");
		DN = (String) nrmConfig.get("user");
		// credential = (String) nrmConfig.get("password");
		credential = null;
		adjustTime = Integer.parseInt
		    ((String) nrmConfig.get("offset"));
	    } catch (Exception ex) {
                ex.printStackTrace();
                System.exit(1);
	    }
		System.err.println("== loadConfig end");
	}
    }
}
